//
// Created by pzw on 24-2-22.
//

#include "file_syscall.h"

#define KDSETLED 0
#define LED_NUM 0
#define LED_CAP 1
#define LED_SCR 2

/**
 * 打开文件或者设备
 *
 * @param path   - 文件路径
 * @param oflags - 文件的打开方式
 * @return int   - 非负整数表示正确打开，并且这个非负整数就是新的文件描述符
 */
int file_syscall::file_sys_open(const char *path, int oflags) {
    return open(path, oflags);
}

/**
 * 打开文件或者设备
 *
 * @param path   - 文件路径
 * @param oflags - 文件的打开方式
 * @param mode   - 模式
 * @return int   - 非负整数表示正确打开，并且这个非负整数就是新的文件描述符
 */
int file_syscall::file_sys_open(const char *path, int oflags, mode_t mode) {
    return open(path, oflags, mode);
}

/**
 * 将缓冲区buf中的前nbytes个字节写入与fildes文件描述符关联的文件里
 *
 * @param fildes  - 关联的文件描述符
 * @param buf     - 缓冲区数据
 * @param nbytes  - buf的前nbytes个字节个数
 * @return size_t - 返回>0：表示写入成功；返回0：表示没有写入任何数据；返回-1：表示写入错误，错误信息会保存到errno，可以查看；
 */
size_t file_syscall::file_sys_write(int fildes, const void *buf, size_t nbytes) {
    return write(fildes, buf, nbytes);
}

/**
 * 从与文件描述符fildes关联的文件里读入nbytes个字节的数据，并把他们放到buf中；
 * 它返回实际读入的字节数，这可能会小于请求的字节数nbytes；
 *
 * @param fildes  - 关联的文件描述符
 * @param buf     - 存放读取数据的缓冲区buffer
 * @param nbytes  - 请求读取的nbytes个字节数量
 * @return size_t - 它返回实际读入的字节数，这可能会小于请求的字节数nbytes；
 */
size_t file_syscall::file_sys_read(int fildes, void *buf, size_t nbytes) {
    return read(fildes, buf, nbytes);
}

/**
 * 关闭文件，即：终止文件描述符fildes与其对应的文件的关联；
 *
 * @param fildes - 关联的文件描述符
 * @return int   - 调用成功返回0；错误返回-1；
 */
int file_syscall::file_sys_close(int fildes) {
    return close(fildes);
}

/**
 * func：ioctl对文件描述符fildes引用的对象执行cmd参数中给出的操作；
 * 也就是说，这个函数是什么功能，取决于cmd中的定义的操作，所以说，这个函数是一个大杂烩函数；
 *
 * @param fildes - 文件描述符
 * @param cmd    - 要对fildes进行操作的cmd
 * @param args   - 根据特定设备所支持的操作不同，可能还有其他可变参数
 * @return int   - 成功与否
 */
int file_syscall::file_sys_ioctl(int fildes, int cmd, ...) {
    va_list args;
    /*这里表示可变参数是从cmd末尾开始的*/
    va_start(args, cmd);
    int ret = ioctl(fildes, cmd, args);
    va_end(args);
    return ret;
}

/**
 * lseek系统调用对文件描述符fildes的读写指针进行设置；
 * 也就是说，你可以用他来设置文件的下一个读写位置；
 * 读写指针既可以被设置为文件中的某个绝对位置，也可以把它设置为相对于当前位置或文件尾部的某个相对位置；
 *
 * @param fildes - 文件描述符
 * @param offset - offset参数用来指定位置
 * @param whence - whence参数用来定义该偏移值offset得用法，whence可以取下列值：
 * @param whence - SEEK_SET: offset是一个绝对位置
 * @param whence - SEEK_CUR: offset是一个相对于当前位置的一个相对位置
 * @param whence - SEEK_END: offset是一个相对于文件尾部的一个相对位置
 * @return int   - lseek返回从文件头到文件指针被设置处的字节偏移值，失败时返回-1；参数off_t是一个与具体实现有关的整数类型；
 */
off_t file_syscall::file_sys_lseek(int fildes, off_t offset, int whence) {
    return lseek(fildes, offset, whence);
}

/**
 * 返回与打开的文件描述符符相关的文件的状态信息，该信息将会写到一个buf结构中，buf的地址以参数的形式传递给fstat；
 *
 * @param fildes - 文件描述符
 * @param buf    - 文件信息地址
 * @return int   - off_t
 */
off_t file_syscall::file_sys_fstat(int fildes, struct stat *buf) {
    return fstat(fildes, buf);
}

/**
 * 返回通过文件名path查到的状态信息，这个是普通文件的；
 *
 * @param path - 普通文件名，或者叫路径
 * @param buf  - 文件信息地址
 * @return int - off_t
 */
off_t file_syscall::file_sys_stat(const char *path, struct stat *buf) {
    return stat(path, buf);
}

/**
 *返回通过文件名path查到的状态信息，这个是符号链接文件的；
 *
 * @param path - 符号链接文件名
 * @param buf  - 文件信息地址
 * @return int - off_t
 */
off_t file_syscall::file_sys_lstat(const char *path, struct stat *buf) {
    return lstat(path, buf);
}

/**
 * dup系统调用提供了一种复制文件描述符的方法，是我们能够通过两个或者更多个不同的文件描述符来访问同一个文件；
 *
 * @param fildes - 当前基本文件描述符
 * @return int   - 复制的新的文件描述符
 */
int file_syscall::file_sys_dup(int fildes) {
    return dup(fildes);
}

/**
 * dup系统调用提供了一种复制文件描述符的方法，是我们能够通过两个或者更多个不同的文件描述符来访问同一个文件；
 *
 * @param fildes  - 基本文件描述符
 * @param fildes2 - 要复制的文件描述符，直接指定，这就是dup和dup2的区别
 * @return int    - 复制成功与否
 */
int file_syscall::file_sys_dup2(int fildes, int fileds2) {
    return dup2(fildes, fileds2);
}

int file_syscall_main() {
    char buf;
    int in, out;
    file_syscall fileSyscall{};

    in = fileSyscall.file_sys_open("", O_RDONLY);
    out = fileSyscall.file_sys_open("", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);

    while (fileSyscall.file_sys_read(in, &buf, 1)) {
        fileSyscall.file_sys_write(out, &buf, 1);
    }

    // 使用ioctl模拟打开LED灯
    fileSyscall.file_sys_ioctl(in, KDSETLED, LED_NUM | LED_CAP | LED_SCR);

    fileSyscall.file_sys_close(in);
    fileSyscall.file_sys_close(out);

    return 0;
}